<template>
<transition
  name="bounce"
  enter-active-class="bounceInDown"
  leave-active-class="bounceOutUp">
  <div class="chat-wrapper">
    <!-- 顶部导航 -->
    <v-header ref="top" title="智能机器人"></v-header>

    <div :style="{height: mainHeight + 'px'}" style="position: relative;">
      <!-- 聊天区域 -->
      <scroll v-show="!showTag" ref="scroll" :data="chatMsgList" style="overflow: hidden; height: 100%">
        <ul class="message">
          <li v-for="(msg, index) in chatMsgList" :key="index" :class="msg.type">
            <p class="time" v-if="msg.sendTimeStr !== ''"> <span>{{msg.sendTimeStr}}</span></p>
            <div class="main">
              <img v-lazy="msg.headUrl" class="avatar"/>
              <div class="text" v-html="msg.content"></div>
            </div>
          </li>
        </ul>
      </scroll>

      <!-- 热门标签 -->
      <transition
        name="bounce"
        enter-active-class="bounceInUp"
        leave-active-class="bounceOutDown">
        <div v-if="showTag" class="hotTag" style="position: relative; height: 100%;">
          <tag-cloud :data="hotTag" @clickItem="clickTagItem"></tag-cloud>
          <div class="tags">
            <p v-for="item in categoryList" :key="item.id" :class="{active: item.id === currentCategory}" @click="clickCategory(item.id)">{{item.name}}</p>
          </div>
        </div>
      </transition>
    </div>

    <!-- 底部输入框 -->
    <div class="bottom" ref="bottom">
      <div style="display: flex;">
        <div class="icon" @click="toggleInput">
          <i v-show="!showVoice" class="fa fa-microphone" aria-hidden="true"></i>
          <i v-show="showVoice" class="fa fa-keyboard-o" aria-hidden="true"></i>
        </div>
        <v-touch
          v-show="showVoice"
          @press="press"
          @pressup="pressup"
          @pan="pan"
          :style="{width: inputWidth + 'px'}"
          class="voiceInput"
          :class="{voiceInputActive: voiceToast.type !== 'normal'}">{{voiceToast.button}}</v-touch>
        <textarea v-show="!showVoice" @keyup.enter="sendMessage"
          @focus="contentFocus"
          @blur="contentBlur"
          class="input"
          ref="textarea"
          v-model.trim="inputContent"
          :style="{width: inputWidth + 'px'}"></textarea>
        <div class="icon iconActive" v-if="inputContent != ''" @click="sendMessage">
          <i class="fa fa-paper-plane-o" aria-hidden="true"></i>
        </div>
        <div class="icon" v-if="inputContent == ''" @click="showTag = !showTag">
          <i class="fa fa-bars" aria-hidden="true" :class="{tagActive: showTag}"></i>
        </div>
      </div>
    </div>

    <!-- 语音Toast -->
    <div class="toast" v-show="showToast">
      <div class="img">
        <img v-for="(img, index) in voiceToast.imgs" :key="index" :src="img"/>
      </div>
      <p :class="{cancel: voiceToast.type === 'cancel'}">{{voiceToast.text}}</p>
    </div>
  </div>
</transition>
</template>

<script>
import { localUser } from '@/assets/js/local'
import { isIOS, isIphoneX } from '@/assets/js/brower'
import chatIcon from '@/assets/json/chatIcon.json'
import NativeBridge from '@/assets/js/nativeBridge'
import chatAIApi from '@/api/chatAIApi'
import axios from '@/api/axiosApi'

import VHeader from '@/components/v-header/v-header'
import Scroll from '@/components/scroll/scroll'
import TagCloud from '@/components/tag-cloud/tag-cloud'
import AIAvatar from '@/assets/img/AI_avatar.png'
import defaultAvatar from '@/assets/img/default_avatar.png'

export default {
  name: 'ChatAI',
  data () {
    return {
      mainHeight: '',
      inputContent: '',
      softHeight: 0,   // 软键盘高度
      statusBarHeight: 24,   // 状态栏高度
      marginBottom: 0,
      chatMsgList: [],
      currentCategory: '',
      categoryList: [],
      hotTag: ['凯里市', '从江县', '台江县', '剑河县', '丹寨县', '榕江县', '施秉县', '镇原县'],    // 热门标签
      chatIcon,
      voiceToast: {
        imgs: [],
        text: ''
      },
      showToast: false,
      showVoice: false,
      showTag: false
    }
  },
  created () {
    this.inputWidth = window.innerWidth - 55 - 24 - 12
    if (isIphoneX()) {
      this.statusBarHeight = 44
      this.marginBottom = 34
      this.topHeight = 74
    } else {
      this.topHeight = 60
    }
    // 软键盘收缩监听
    window._Native_heightChange = (height, visibleHeight) => {
      // 软键盘的收缩导致的高度变化。Android与IOS同样处理
      const scale = height / window.innerHeight // 1px在移动端所占像素点
      if ((height - visibleHeight) / scale <= this.statusBarHeight) {
        this.softHeight = 0
        this.$refs.top.$el.style.marginTop = '0'
        if (isIphoneX()) {
          this.marginBottom = 34
        }
      } else {
        this.marginBottom = 0
        if (isIOS()) {
          this.softHeight = (height - visibleHeight) / scale
        } else {
          this.softHeight = (height - visibleHeight) / scale - this.statusBarHeight
        }
        this.showEmotion = false
      }
      this.computedMainHeight()
    }
    this.voiceToast = {
      imgs: [chatIcon.recorder, chatIcon.v1],
      text: '手指上划，取消发送',
      button: '按住  说话',
      type: 'normal'
    }

    this._getTagCategory()
  },
  mounted() {
    this.autoTextarea(90, 0)
    setTimeout(() => {
      this.computedMainHeight(true)
    }, 300)

    this.chatMsgList.push({
      id: 'xiaohai',
      type: 'from',
      content: '我是小嗨，为您解决出行烦恼',
      nickname: '小嗨',
      headUrl: AIAvatar,
      sendTime: new Date().getTime()
    })
  },
  components: {
    VHeader, Scroll, TagCloud
  },
  methods: {
    _getTagCategory () {
      axios.get(this, '/v1/intelligenceCategory', {
        pageSize: 10
      }, (data) => {
        this.categoryList = data
        this.currentCategory = data[0].id
        this._getHotTag()
      })
    },
    _getHotTag () {
      axios.get(this, `/v1/intelligenceTag/${this.currentCategory}`, null, (data) => {
        this.hotTag = data
      })
    },
    clickCategory(id) {
      this.currentCategory = id
      this._getHotTag()
    },
    contentFocus (e) {
      if (isIOS()) {
        this.$refs.bottom.style.position = 'fixed'
      }
      const _self = this
      // 实时计算高度
      this.heightTimer = setInterval(() => {
        _self.computedMainHeight()
      }, 200)
    },
    contentBlur () {
      clearInterval(this.heightTimer)
    },
    computedMainHeight(first) {
      this.$refs.bottom.style.position = 'relative'
      this.$refs.bottom.scrollIntoView()
      this.$refs.bottom.scrollIntoViewIfNeeded()
      this.$refs.bottom.style.bottom = '5px'
      this.mainHeight = window.innerHeight - this.topHeight - this.$refs.bottom.clientHeight - this.softHeight - this.marginBottom
      let time = 300
      if (first) {
        time = 0
      }
      this.$nextTick(() => {
        if (this.$refs.scroll) {
          setTimeout(() => {
            if (this.$refs.scroll) {
              this.$refs.scroll.scroll.refresh()
              this.$refs.scroll.scroll.scrollTo(0, this.$refs.scroll.scroll.maxScrollY, time)
            }
          }, time)
        }
      })
    },
    sendMessage () {
      if (this.inputContent === '') {
        return
      }
      const value = this.inputContent
      this.inputContent = ''
      this.$refs.textarea.focus()
      let user = localUser.get()
      if (user == null) user = {}
      this.chatMsgList.push({
        id: user.id || '',
        type: 'to',
        content: value,
        nickname: user.nickname || '',
        headUrl: user.headUrl || defaultAvatar,
        sendTime: new Date().getTime()
      })
      // TODO 定位信息处理
      const _self = this
      chatAIApi(value, '', user.id || '').then((text) => {
        _self.chatMsgList.push({
          id: 'xiaohai',
          type: 'from',
          content: text,
          nickname: '小嗨',
          headUrl: AIAvatar,
          sendTime: new Date().getTime()
        })
      }).catch((e) => {
        this.$vux.toast.text(e.message, 'middle')
      })
    },
    autoTextarea (maxHeight, extra) {
      // textarea自适应高度
      const elem = this.$refs.textarea
      extra = extra || 0
      const isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX' in window
      const isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera')
      const addEvent = function (type, callback) {
        elem.addEventListener ? elem.addEventListener(type, callback, false) : elem.attachEvent('on' + type, callback)
      }
      const getStyle = elem.currentStyle ? function (name) {
        var val = elem.currentStyle[name]
        if (name === 'height' && val.search(/px/i) !== 1) {
          var rect = elem.getBoundingClientRect()
          return rect.bottom - rect.top - parseFloat(getStyle('paddingTop')) - parseFloat(getStyle('paddingBottom')) + 'px'
        }
        return val
      } : function (name) {
        return getComputedStyle(elem, null)[name]
      }
      const minHeight = 40
      elem.style.resize = 'none'
      const change = function () {
        let height
        let padding = 0
        let style = elem.style
        if (elem._length === elem.value.length) return
        elem._length = elem.value.length
        if (!isFirefox && !isOpera) {
          padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom'))
        }

        elem.style.height = minHeight + 'px'
        if (elem.scrollHeight > minHeight) {
          if (maxHeight && elem.scrollHeight > maxHeight) {
            height = maxHeight - padding
            style.overflowY = 'auto'
          } else {
            height = elem.scrollHeight - padding
            style.overflowY = 'hidden'
          };
          style.height = height + extra + 'px'
          elem.currHeight = parseInt(style.height)
        }
      }
      addEvent('propertychange', change)
      addEvent('input', change)
      addEvent('focus', change)
      change()
    },
    press () {
      if (this.recordTimer) {
        clearTimeout(this.recordTimer)
        this.recordTimer = null
      }
      this.showToast = true
      this.voiceToast = {
        type: 'normal',
        imgs: [chatIcon.recorder, chatIcon.v1],
        text: '手指上划，取消发送',
        button: '松开  结束'
      }
      this.startRecordTime = new Date()
      NativeBridge.recognizerVoice((str) => {
        this.inputContent += str
        this.showVoice = false
      }, (volume) => {
        volume = Math.ceil((volume + 5) / 5)
        if (this.showToast && (this.voiceToast.type === 'normal' || this.voiceToast.type === 'listen')) {
          this.voiceToast = {
            type: 'listen',
            imgs: [chatIcon.recorder, chatIcon['v' + volume]],
            text: '手指上划，取消发送',
            button: '松开  结束'
          }
        }
      })
    },
    pan (e) {
      if (e.distance > 120) {
        this.voiceToast = {
          type: 'cancel',
          imgs: [this.chatIcon.cancel],
          text: '松开手指，取消发送',
          button: '松开手指，取消发送'
        }
      } else {
        this.voiceToast = {
          type: 'listen',
          imgs: [chatIcon.recorder, chatIcon.v1],
          text: '手指上划，取消发送',
          button: '松开  结束'
        }
      }
      if (e.isFinal) {
        this.showToast = false
        if (e.distance <= 120) {
          this.sendMessage()
          NativeBridge.stopRecognizerVoice()
          this.toggleInput()
        } else {
          // 取消发送
          NativeBridge.cancelRecognizerVoice()
        }
        this.voiceToast = {
          type: 'normal',
          imgs: [chatIcon.recorder, chatIcon.v1],
          text: '手指上划，取消发送',
          button: '按住  说话'
        }
      }
    },
    pressup () {
      if (this.moment().diff(this.moment(this.startRecordTime)) < 1000) {
        this.voiceToast = {
          type: 'tooShort',
          imgs: [chatIcon.voice_to_short],
          text: '录制时间太短',
          button: '松开  结束'
        }
        this.recordTimer = setTimeout(() => {
          this.showToast = false
          this.voiceToast = {
            type: 'normal',
            imgs: [chatIcon.recorder, chatIcon.v1],
            text: '手指上划，取消发送',
            button: '按住  说话'
          }
        }, 1000)
      } else {
        NativeBridge.stopRecognizerVoice()
        this.sendMessage()
        this.showToast = false
        this.voiceToast = {
          type: 'normal',
          imgs: [chatIcon.recorder, chatIcon.v1],
          text: '手指上划，取消发送',
          button: '按住  说话'
        }
        this.toggleInput()
      }
    },
    toggleInput () {
      this.showVoice = !this.showVoice
      if (!this.showVoice) {
        this.$refs.textarea.focus()
      }
    },
    clickTagItem (tag) {
      this.inputContent = tag.name
      this.showTag = false
      this.sendMessage()
    }
  },
  watch: {
    chatMsgList (newVal, oldVal) {
      // 处理时间
      newVal.forEach((item, index) => {
        if (this.moment().diff(this.moment(item.sendTime), 'days') <= 1) {
          item.sendTimeStr = this.moment(item.sendTime).format('HH:mm')
        } else {
          item.sendTimeStr = this.moment(item.sendTime).format('YYYY-MM-DD HH:mm')
        }
        if (index > 0) {
          if (this.moment(item.sendTime).diff(this.moment(newVal[index - 1].sendTime), 'minutes') < 4) {
            item.sendTimeStr = ''
          }
        }
      })
      const item = newVal[newVal.length - 1]
      if (item.type === 'from') {
        NativeBridge.speechVoice(item.content)
      }
    }
  },
  destroyed () {
    NativeBridge.stopSpeechVoice()
    window._Native_heightChange = () => {}
  },
  deactivated () {
    NativeBridge.stopSpeechVoice()
    window._Native_heightChange = () => {}
  }
}
</script>

<style lang="stylus" scoped>
@import '~@/assets/css/variable.styl'
.bounceInDown
.bounceOutUp
  animation-duration .6s
.chat-wrapper {
  background-color: @color-background;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  .message {
    padding: 10px 15px;
    list-style: none;
    li {
      margin-bottom: 15px;
      .time {
        margin: 4px 0;
        text-align: center;
        span{
          display: inline-block;
          padding: 4px 18px;
          font-size: 13px;
          color: #fff;
          border-radius: 2px;
          background-color: #dcdcdc;
          line-height: 1;
        }
      }
      .avatar {
        float: left;
        width: 40px;
        height: 40px;
        margin: 0 10px 0 0;
        border-radius: 3px;
      }
      .text {
        display: inline-flex;
        position: relative;
        padding: 3px 10px;
        max-width: calc(100% - 100px);
        min-height: 30px;
        line-height: 1.8;
        font-size: 15px;
        text-align: left;
        word-break: break-all;
        background-color: #ffffff;
        border-radius: 4px;
        align-items: center;

        &:before {
          content: " ";
          position: absolute;
          top: 9px;
          right: 100%;
          border: 6px solid transparent;
          border-right-color: #ffffff;
        }
      }
    }
    .to {
      text-align: right;
      .avatar {
        float: right;
        margin: 0 0 0 10px;
      }
      .text {
        background-color: #b2e281;
        &:before {
          right: inherit;
          left: 100%;
          border-right-color: transparent;
          border-left-color: #b2e281;
        }
      }
    }
  }

  .tags {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 9px 9px 0 0;

    p {
      background: #fff;
      color: #666;
      padding: 5px 9px;
      border-radius: 4px;
      float: left;
      margin-left: 9px;
      margin-bottom: 9px;
      font-size: 14px;
    }

    .active {
      background: $color-theme;
      color: #fff;
    }
  }

  .tagActive {
    color: $color-theme
  }

  .bottom {
    position: absolute;
    bottom: -223px;
    left: 0;
    right: 0;
    padding: 5px 12px;
    background: #eee;
    border-top: 1px solid $color-border;
    background: #ffffff;

    .icon {
      width: 40px;
      height: 40px;
      min-width: 40px;
      border-radius: 50%;
      border: 1px solid $color-border;
      background: $color-background;
      color: #333;
      text-align: center;
      line-height: 40px;
      font-size: 22px;
    }

    .iconActive {
      background: #3cc51f;
      color: #fff;
    }

    .voiceInput {
      position: relative;
      margin-left: 12px;
      margin-right: 12px;
      border-radius: 3px;
      border: 1px solid $color-border;
      text-align: center;
      color: #666;
      line-height: 40px;
      background: $color-background;
    }

    .voiceInputActive {
      background: #e1e1e1;
    }

    .input {
      position: relative;
      margin-left: 12px;
      margin-right: 12px;
      border-radius: 3px;
      border: 1px solid $color-border;
      padding: 9px 7px 6px;
      word-break: break-all;
      flex: 1;
      resize: none;
      box-sizing: border-box;
      outline: none;
      font-size: 16px;
      line-height: 1.3;
      display: block;
      overflow-y: visible;
    }

    .send {
      width: 62px;
      height: 36px;
      line-height: 36px;
      text-align: center;
      border-radius: 3px;
      background: $color-theme-3;
      color: #fff;
      font-size: 16px;
      margin-top: 2px;
    }
  }

  .toast {
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 5000;
    transform: translateX(-50%) translateY(-50%);
    padding: 15px;
    background: rgba(0, 0, 0, 0.4);
    border-radius: 5px;
    color: #fff;
    font-size: 14px;
    max-width: 60%;
    text-align: center;

    .img {
      text-align: center;
      img {
        height: 90px;
      }
    }
    p {
      border-radius: 3px;
      padding: 6px 9px;
    }
    .cancel {
      background: rgba(146, 9, 9, 0.5)
    }
  }
}
</style>

